#include "Renderer.h"

int Renderer::elapsedTime = 0;

GLUquadric *Renderer::quadric = NULL;
int Renderer::currentMouseX = 0;
int Renderer::currentMouseY = 0;
GLfloat Renderer::lightPos[];

void Renderer::Init(int argc, char** argv, char* title, int width, int height)
{
    glutInit(&argc, argv);
    glutInitWindowSize(width, height);
    glutInitWindowPosition((glutGet(GLUT_SCREEN_WIDTH)-width)/2, (glutGet(GLUT_SCREEN_HEIGHT)-height)/2);
    glutInitDisplayMode(GLUT_RGBA|GLUT_DOUBLE|GLUT_DEPTH);
    int mainHandle = glutCreateWindow(title);
    glutSetWindow(mainHandle);
    glutDisplayFunc(RenderCallback);
    glutReshapeFunc(ReshapeCallback);
    glutIdleFunc(IdleCallback);
    glutKeyboardFunc(KeyboardCallback);
    glutSpecialFunc(KeyboardSpecialCallback);
    glutMotionFunc(MousePressedMovementCallback);
    glutMouseFunc(MouseClickCallback);
    glutPassiveMotionFunc(MouseMovementCallback);

    glClearColor(0.2f, 0.4f, 0.5f, 1.0);

    glShadeModel(GL_SMOOTH);
    glEnable(GL_DEPTH_TEST);
    glEnable(GL_CULL_FACE);

    GLfloat position[] = {0.0f, 1.0f, 0.0f, 0.0f};
    GLfloat diffuse[] = {0.8f, 0.8f, 0.8f, 1.0f};
    GLfloat ambient[] = {0.15f, 0.15f, 0.15f, 1.0f};
    GLfloat specular[] = {1.0f, 1.0f, 1.0f, 1.0f};
    glLightfv(GL_LIGHT0, GL_POSITION, position);
    glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuse);
    glLightfv(GL_LIGHT0, GL_AMBIENT, ambient);
    glLightfv(GL_LIGHT0, GL_SPECULAR, specular);

    glEnable(GL_LIGHTING);
    glEnable(GL_LIGHT0);

    lightPos[0] = 1.0f; lightPos[1] = -1.0f; lightPos[2] = 1.0f; lightPos[3] = 0.0f;
    glLoadIdentity();
    glLightfv(GL_LIGHT0, GL_POSITION, lightPos);

    glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE);

    float mcolor[] = {1.0f, 1.0f, 1.0f, 1.0f};
    glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, mcolor);

    glEnable(GL_COLOR_MATERIAL);

    quadric = gluNewQuadric();
    gluQuadricNormals(quadric, GL_SMOOTH);
    gluQuadricTexture(quadric, GL_TRUE);
}

void Renderer::Run()
{
    glutMainLoop();
}

void Renderer::Exit()
{
    gluDeleteQuadric(quadric);
}

void Renderer::ResetLightPosition()
{
    glLightfv(GL_LIGHT0, GL_POSITION, lightPos);
}

void Renderer::RenderCallback()
{
    PhysxLogic::SimulateAndRender();
}

void Renderer::IdleCallback()
{
    glutPostRedisplay();
}

void Renderer::ReshapeCallback(int width, int height)
{
    glViewport(0, 0, width, height);
}

void Renderer::KeyboardCallback(unsigned char key, int x, int y)
{
    PhysxLogic::HandleKeyboardEvent(key, x, y);
}

void Renderer::KeyboardSpecialCallback(int key, int x, int y)
{
    PhysxLogic::HandleKeyboardSpecialEvent(key, x, y);
}

void Renderer::MouseClickCallback(int button, int state, int x, int y)
{
    PhysxLogic::HandleMouseClickEvent(button, state, x, y);
}

void Renderer::MousePressedMovementCallback(int x, int y)
{
    int dx = x - currentMouseX;
    int dy = y - currentMouseY;

    currentMouseX = x;
    currentMouseY = y;

    PhysxLogic::HandleMousePressedMovementEvent(x, y, dx, dy);
}

void Renderer::MouseMovementCallback(int x, int y)
{
    currentMouseX = x;
    currentMouseY = y;
}

void Renderer::DrawBox(const NxVec3 &dim)
{
    glBegin(GL_QUADS);

    //front
    glNormal3f(0.0f, 0.0f, 1.0f);
    glTexCoord2f(0.0f, 0.0f);
    glVertex3f(-dim.x, -dim.y, dim.z);
    glTexCoord2f(1.0f, 0.0f);
    glVertex3f(dim.x, -dim.y, dim.z);
    glTexCoord2f(1.0f, 1.0f);
    glVertex3f(dim.x, dim.y, dim.z);
    glTexCoord2f(0.0f, 1.0f);
    glVertex3f(-dim.x, dim.y, dim.z);

    //right
    glNormal3f(1.0f, 0.0f, 0.0f);
    glTexCoord2f(0.0f, 0.0f);
    glVertex3f(dim.x, -dim.y, dim.z);
    glTexCoord2f(1.0f, 0.0f);
    glVertex3f(dim.x, -dim.y, -dim.z);
    glTexCoord2f(1.0f, 1.0f);
    glVertex3f(dim.x, dim.y, -dim.z);
    glTexCoord2f(0.0f, 1.0f);
    glVertex3f(dim.x, dim.y, dim.z);

    //back
    glNormal3f(0.0f, 0.0f, -1.0f);
    glTexCoord2f(0.0f, 0.0f);
    glVertex3f(dim.x, -dim.y, -dim.z);
    glTexCoord2f(1.0f, 0.0f);
    glVertex3f(-dim.x, -dim.y, -dim.z);
    glTexCoord2f(1.0f, 1.0f);
    glVertex3f(-dim.x, dim.y, -dim.z);
    glTexCoord2f(0.0f, 1.0f);
    glVertex3f(dim.x, dim.y, -dim.z);

    //left
    glNormal3f(-1.0f, 0.0f, 0.0f);
    glTexCoord2f(0.0f, 0.0f);
    glVertex3f(-dim.x, -dim.y, -dim.z);
    glTexCoord2f(1.0f, 0.0f);
    glVertex3f(-dim.x, -dim.y, dim.z);
    glTexCoord2f(1.0f, 1.0f);
    glVertex3f(-dim.x, dim.y, dim.z);
    glTexCoord2f(0.0f, 1.0f);
    glVertex3f(-dim.x, dim.y, -dim.z);

    //top
    glNormal3f(0.0f, 1.0f, 0.0f);
    glTexCoord2f(0.0f, 0.0f);
    glVertex3f(-dim.x, dim.y, dim.z);
    glTexCoord2f(1.0f, 0.0f);
    glVertex3f(dim.x, dim.y, dim.z);
    glTexCoord2f(1.0f, 1.0f);
    glVertex3f(dim.x, dim.y, -dim.z);
    glTexCoord2f(0.0f, 1.0f);
    glVertex3f(-dim.x, dim.y, -dim.z);

    //bottom
    glNormal3f(0.0f, -1.0f, 0.0f);
    glTexCoord2f(0.0f, 0.0f);
    glVertex3f(dim.x, -dim.y, dim.z);
    glTexCoord2f(1.0f, 0.0f);
    glVertex3f(-dim.x, -dim.y, dim.z);
    glTexCoord2f(1.0f, 1.0f);
    glVertex3f(-dim.x, -dim.y, -dim.z);
    glTexCoord2f(0.0f, 1.0f);
    glVertex3f(dim.x, -dim.y, -dim.z);

    glEnd();
}

void Renderer::DrawCapsule(const float height, const float radius)
{
    glPushMatrix();
    glTranslatef(0.0f, height*0.5f, 0.0f);
    glutSolidSphere(radius, 24, 24);
    glPopMatrix();

    glPushMatrix();
    glTranslatef(0.0f, height*0.5f, 0.0f);
    glRotatef(90.0f, 1.0f, 0.0f, 0.0f);
    gluCylinder(quadric, radius, radius, height, 24, 24);
    glPopMatrix();

    glPushMatrix();
    glTranslatef(0.0f, -height*0.5f, 0.0f);
    glutSolidSphere(radius, 24, 24);
    glPopMatrix();	
}

void Renderer::DrawPlane(const float size, const int subDivX, const int subDivZ)
{
    glBegin(GL_QUADS);
    for(int x = -subDivX; x <= subDivX; ++x) {
        for(int z = -subDivZ; z <= subDivZ; ++z) {
            float ox = size * 2.0f * x;
            float oz = size * 2.0f * z;

            glNormal3f(0.0f, 1.0f, 0.0f);
            glTexCoord2f(0.0f, 0.0f);
            glVertex3f(-size +ox, 0.0f, size + oz);
            glTexCoord2f(1.0f, 0.0f);
            glVertex3f(size + ox, 0.0f, size + oz);
            glTexCoord2f(1.0f, 1.0f);
            glVertex3f(size + ox, 0.0f, -size + oz);
            glTexCoord2f(0.0f, 1.0f);
            glVertex3f(-size + ox, 0.0f, -size + oz);
        }
    }
    glEnd();
}
